package com.throttle.raildroid;
import java.net.*;
import java.io.*;

class MessageLibrary {
	// Switch control
	// Op codes
	private static final byte SW_REQ = 0xB0-256;    // request a switch change
	private static final byte SW_REP = 0xB1-256;    // report a switch change
	// Actions
	private static final byte THROW  = 0x10;
	private static final byte CLOSE  = 0x30;
	private static final byte CLOSED = 0x10;
	// Extended
	private static final byte PUTSWITCHSTATE = 4;
	private static final byte GETSWITCHSUCC  = 6;
	private static final byte PUTSWITCHSUCC  = 7;
	private static final byte GETSWITCHSTATE = 22;
	
	// Train control
	// Op codes
	private static final byte REG_SLOT 	= 0xBA-256; // register a slot
	private static final byte BAD_SLOT 	= 0xB4-256; // insufficient slots
	private static final byte SLOT_REQ 	= 0xBF-256; // request for slot data
	private static final byte SLOT_REP 	= 0xE7-256;	// slot data response
	private static final byte SET_SPEED = 0xA0-256;	// set train speed
	private static final byte SET_MISC 	= 0xA1-256; // set direction, horn, bell, lights
	private static final byte SET_SOUND = 0xA2-256; // set/unset mute
	// Actions
	private static final byte FORWARD 	= 0x00;
	private static final byte BACKWARD 	= 0x20;
	private static final byte LIGHTSON 	= 0x10;
	private static final byte LIGHTSOFF = 0x00;		// Can have a general
	private static final byte HORNON 	= 0x02;		// "off" setting instead of
	private static final byte HORNOFF 	= 0x00;		// multiple specific ones
	private static final byte BELLON 	= 0x01;
	private static final byte BELLOFF 	= 0x00;
	private static final byte MUTEON 	= 0x08;
	private static final byte MUTEOFF 	= 0x00;
	// Extended
	private static final byte PUTTRAINSTATE = 1;
	private static final byte PUTTRAINPOS 	= 2;
	private static final byte LOCOINIT 		= 8;
	private static final byte INITOUCOME 	= 9;
	private static final byte PUTTRAININFO 	= 21;
	
	// Misc
	// Op codes
	private static final byte INPUT_REP = 0xB2-256;  // report sensor fired
	// Extended
	private static final byte PUTSECTIONSTATE 	= 3;
	private static final byte PUTSENSORSTATE 	= 5;
	private static final byte READLAYOUT 		= 10;
	private static final byte LAYOUTREP 		= 11;
	private static final byte SAVESTATE 		= 12;
	private static final byte SAVEREP 			= 13;
	private static final byte RESTORESTATE 		= 14;
	private static final byte RESTOREREP 		= 15;
	private static final byte TRYTOMOVEAGAIN 	= 16;
	private static final byte FRONTSENSORFIRED 	= 17;
	private static final byte BACKSENSORFIRED 	= 18;
	private static final byte SENSORERROR 		= 19;
	private static final byte LOSERESERVATION 	= 20;
	private static final byte EXTENDED 			= 0;
	
	private static RailEventHandler rHand = new RailEventHandler();
	private static OutputStream out = null;
	private static boolean init = false;
	
	// connects to a socket and returns the socket
	// also initializes an output stream if MessageLibrary will be used to send messages
	public static Socket connect(String addr, int port){
		Socket mysocket = null;
		try {
        	mysocket = new Socket(addr, port);
        	out = mysocket.getOutputStream();
        	init = true;
		}
		catch (java.net.UnknownHostException u) {
			System.out.println("uhe!"); // Display the string.
	    	System.err.println(u);
	    }
	    catch (java.io.IOException e) {
	    	System.out.println("ioex!"); // Display the string.
	    	System.err.println(e);
	    }
		return mysocket;
	}

	// translate an array of bytes into strings for output
	// assumes input is correct
	// ignores checksum bytes for now
	// first two bytes of message are considered to be information on how long the message is and are ignored
	public static String decrypt(byte[] msg){
		return decrypt(msg, null);
	}
	
	// assigns an outputstream for use within the function if needed (Slot_rep)
    public static String decrypt(byte[] msg, OutputStream otherout) {
    	String msg1;
    	int state;
    	int sw;
    	int slot;
    	
    	switch(msg[2]){
    		case SW_REP: 
    			//A switch has been changed
    			//[SW_REP][Switch][State][Checksum]
    			sw = 1+(msg[3]+128*(msg[4] & 0x0F));
    			if((msg[4] & CLOSED) == CLOSED)
    				state = 0;
    			else
    				state = 1;
    			msg1 = "switch changed: "+sw+" with state " +state;
    			Switch_report_event sw1 = new Switch_report_event(true);
    			sw1.msg = msg1;
    			sw1.setSwitchNumber(sw);
    			sw1.setSwitchState(state);
    			rHand.fireMyEvent(sw1);
    			return msg1;
    			//break;
    		case SW_REQ:
    			//Another throttle is attempting to change a switch
    			//[SW_REP][Switch][State][Checksum]
    			sw = msg[3];
    			if((msg[4] & CLOSE) == CLOSE)
    				state = 0;
    			else
    				state = 1;
    			Switch_request_event sw2 = new Switch_request_event(true);
    			msg1 = "switch requested: "+sw+" with state " +state;
    			sw2.msg = msg1;
    			sw2.setSwitchNumber(sw);
    			sw2.setSwitchState(state);
    			rHand.fireMyEvent(sw2);
    			return msg1;
    			//break;
    		case BAD_SLOT: 
    			// can be a multi-purpose message, seems to be used
				// for when slots are full
    			msg1 =  "Insufficient slots. Cannot take train";
    			No_slots_event nse = new No_slots_event(true);
    			nse.msg = msg1;
    			rHand.fireMyEvent(nse);
    			//break;
    		case SLOT_REP:  
    			//General information about a train. Sent when requesting information about a train
    			//Must be used with a set out variable
    			//[SLOT_REP][0E][Slot][Stat(registered)][AddrInfo][Speed]
    			//          [Dirf(horn,bell,etc)][Trk][Ss2][AddrInfo2][Snd][Id1][Id2][Checksum]
    			int addr;
    			boolean registered;
    			addr = msg[11]*128+msg[6];
    			registered = (msg[5] & 0x30) == 0x30;
    			slot = msg[4];
    			if(registered){
    				msg1 = "Train Already registered at slot: "+slot+". Attempting to steal.";
    				byte[] newMsg = makeStealTrain(slot, slot);
    				try{
    					if(otherout != null)
    						otherout.write(newMsg);
    					else if(init)
    						out.write(newMsg);
    					else
    						return "No output, cannot steal train";
    				}catch (java.net.UnknownHostException u) {
    		            return "Unknown host exception: decrypt, slot report. "+u.toString();
    		        }
    		        catch (java.io.IOException e) {
    		            return "IO exception: decrypt, slot report. "+e.toString();
    		        }
    			}else
    				msg1 = "Train "+addr+" successfully gotten\nSlot: "+slot;
    			Slot_report_event re = new Slot_report_event(true);
    			re.msg = msg1;
    			re.setRegistered(registered);
    			re.setSlotnumber(slot);
    			re.setTrainAddress(addr);
    			rHand.fireMyEvent(re);
    			return msg1;
    			//break;
    		case SET_SPEED: 
    			//Another throttle is attempting to change the speed of the train
    			//[SET_SPEED][Slot][Speed][Checksum]
    			msg1 = "Speed successfully set to "+msg[4]+" in slot: "+msg[3];
    			Set_speed_event sp = new Set_speed_event(true);
    			sp.msg = msg1;
    			sp.setSlotnumber(msg[3]);
    			sp.setSpeed(msg[4]);
    			rHand.fireMyEvent(sp);
    			return msg1;
    			//break;
    		case SET_MISC:  
    			// Another throttle is attempting to change a misc funtion of the train
    			//[SET_MISC][Slot][Dirf(horn,bell,etc)][Checksum]
    			slot = msg[3];
    			boolean di = false;
    			boolean ho = false;
    			boolean be = false;
    			boolean li = false;
    			if((msg[4] & BACKWARD) == BACKWARD)
    				di = true;
    			if((msg[4] & HORNON) == HORNON)
    				ho = true;
    			if((msg[4] & BELLON) == BELLON)
    				be = true;
    			if((msg[4] & LIGHTSON) == LIGHTSON)
    				li = true;
    			msg1 = "Train at slot: "+slot+" has "+
    					"Direction: "+di+
    					" Horn "+ho+
    					" Bell "+be+
    					" Lights "+li;
    			Set_misc_event dir2 = new Set_misc_event(true);
				Set_misc_event light2 = new Set_misc_event(true);
				Set_misc_event bell2 = new Set_misc_event(true);
				Set_misc_event horn2 = new Set_misc_event(true);
				dir2.msg = "Direction in slot "+slot+" is "+di;
				dir2.setState(di);
				dir2.setType(1);
				dir2.setSlotnumber(slot);
				horn2.msg = "Horn in slot "+slot+" is "+ho;
				horn2.setState(ho);
				horn2.setType(2);
				horn2.setSlotnumber(slot);
				bell2.msg = "Bell in slot "+slot+" is "+be;
				bell2.setState(be);
				bell2.setType(3);
				bell2.setSlotnumber(slot);
				light2.msg = "Light in slot "+slot+" is "+li;
				light2.setState(li);
				light2.setType(4);
				light2.setSlotnumber(slot);
				rHand.fireMyEvent(dir2);
				rHand.fireMyEvent(horn2);
				rHand.fireMyEvent(bell2);
				rHand.fireMyEvent(light2);
				return msg1;
    			//break;
    		case SET_SOUND: 
    			//Another throttle is attempting to mute a train
    			//[SET_SOUND][Slot][Mute][Checksum]
    			boolean mute;
    			slot = msg[3];
    			if((msg[4] & MUTEON) == MUTEON)
    				mute = true;
    			else
    				mute = false;
    			msg1 = "Mute status on slot: "+msg[3]+" is "+mute;
    			Set_mute_event mu = new Set_mute_event(true);
    			mu.msg = msg1;
    			mu.setMuted(mute);
    			mu.setSlotnumber(slot);
    			rHand.fireMyEvent(mu);
    			return msg1;
    			//break;
    		case INPUT_REP:  
    			// A sensor has fired
    			//[INPUT_REP][S#1][S#2][Checksum]
    			int a, b, c;
    			int sensor;
    			a = msg[3];
    			b = msg[4] & 0x0F;
    			c = 2*(128*b+a+1);
    			if((msg[4] & 0x20) == 0x20)
    				sensor = c;
    			else
    				sensor = c-1;
    			msg1 = "Sensor report: "+sensor;
    			Input_report_event inprep = new Input_report_event(true);
    			inprep.msg = msg1;
    			inprep.setSensor(sensor);
    			rHand.fireMyEvent(inprep);
    			return msg1;
    			//break;
    			
    		// For extended messages
    		case EXTENDED:			// ALL UNTESTED
    			switch (msg[3]){
    				case PUTSWITCHSTATE:
    					//Information about a switch, input from sending getSwitches()
    					//[00][PUTSWITCHSTATE][Sw#][State]
    					sw = msg[4];
	    				if(msg[5] == 0) 		// closed
	    					state = 0;
	    				else if(msg[5] == 1) 	// thrown
	    					state = 1;
	    				else 					//unknown
	    					state = 2; 
	    				msg1 = "Switch "+sw+" is in state "+state;
	    				Switch_report_event sw3 = new Switch_report_event(true);
	    				sw3.msg = msg1;
	    				sw3.setSwitchNumber(sw);
	    				sw3.setSwitchState(state);
	    				return msg1;
    				case PUTTRAININFO:
    					//Information about a train, sent when any of the variables changes
    					//[00][PUTTRAININFO][Slot#][Speed][Direction][Light][Bell][Horn][Mute]
    					// types are 	1: direction
    					// 				2: horn
    					//				3: bell
    					//				4: light
    					slot = msg[4];
    					int speed = msg[5];
    					boolean dir;
    					if(msg[6] == 0)
    						dir = false;
    					else
    						dir = true;
    					boolean light;
    					if(msg[7] == 0)
    						light = false;
    					else
    						light = true;
    					boolean bell;
    					if(msg[8] == 0)
    						bell = false;
    					else
    						bell = true;
    					boolean horn;
    					if(msg[9] == 0)
    						horn = false;
    					else
    						horn = true;
    					boolean muteinf;
    					if(msg[10] == 0)
    						muteinf = false;
    					else 
    						muteinf = true;
    					msg1 = "Train information is:\nSlot: "+slot+
    							"\nSpeed: "+speed+
    							"\nDirection: "+dir+
    							"\nLight: "+light+
    							"\nBell: "+bell+
    							"\nHorn: "+horn+
    							"\nMute: "+muteinf;
    					Set_speed_event sp1 = new Set_speed_event(true);
    					Set_misc_event dir1 = new Set_misc_event(true);
    					Set_misc_event light1 = new Set_misc_event(true);
    					Set_misc_event bell1 = new Set_misc_event(true);
    					Set_misc_event horn1 = new Set_misc_event(true);
    					Set_mute_event mute1 = new Set_mute_event(true);
    					sp1.msg = "Speed in slot "+slot+" is "+speed;
    					sp1.setSlotnumber(slot);
    					sp1.setSpeed(speed);
    					dir1.msg = "Direction in slot "+slot+" is "+dir;
    					dir1.setState(dir);
    					dir1.setType(1);
    					dir1.setSlotnumber(slot);
    					horn1.msg = "Horn in slot "+slot+" is "+horn;
    					horn1.setState(horn);
    					horn1.setType(2);
    					horn1.setSlotnumber(slot);
    					bell1.msg = "Bell in slot "+slot+" is "+bell;
    					bell1.setState(bell);
    					bell1.setType(3);
    					bell1.setSlotnumber(slot);
    					light1.msg = "Light in slot "+slot+" is "+light;
    					light1.setState(light);
    					light1.setType(4);
    					light1.setSlotnumber(slot);
    					mute1.msg = "Mute in slot "+slot+" is "+muteinf;
    					mute1.setMuted(muteinf);
    					mute1.setSlotnumber(slot);
    					rHand.fireMyEvent(sp1);
    					rHand.fireMyEvent(dir1);
    					rHand.fireMyEvent(horn1);
    					rHand.fireMyEvent(bell1);
    					rHand.fireMyEvent(light1);
    					rHand.fireMyEvent(mute1);
    					return msg1;
    				case PUTTRAINSTATE:
    					//Information about whether a train is moving or not, sent when train enters new state
    					//[00][PUTTRAINSTATE][Slot#][State]
    					slot = msg[4];
    					if(msg[5] == 0)
    						state = 0;
    					else if(msg[5] == 1)
    						state = 1;
    					else
    						state = 2;
    					msg1 = "Train at slot: "+slot+" is "+state;
    					Train_state_event tr = new Train_state_event(true);
    					tr.msg = msg1;
    					tr.setSlotnumber(slot);
    					tr.setState(state);
    					rHand.fireMyEvent(tr);
    					return msg1;
    				case PUTTRAINPOS:
    					//Information about where a train currently is, sent when train changes positions via sensor firing
    					// DON'T KNOW HOW THIS WORKS, (how many sensors are there? 2? 3? count? are there two bytes per sensor?)
    					//		Assuming count is how many sensors there are and there are two bytes per sensor, sent as per the INPUT_REP message
    					//[00][PUTTRAINPOS][Slot#][Count][Sensor#]..[Sensor#]
    					slot = msg[4];
    					int size = msg[5]; // assuming count is how many sensors are in the message
    					int[] sensors = new int[size];
    					int d,e,f,x;
    					x = 6;
    					for(int i=0; i<size; i++){
    						d = msg[x];
    						x++;
        	    			e = msg[x] & 0x0F;
    						x++;
        	    			f = 2*(128*e+d+1);
    						if((msg[x-1] & 0x20) == 0x20)
        	    				sensors[i] = f;
        	    			else
        	    				sensors[i] = f-1;
    					}
    					msg1 = "Train at slot: "+slot+"is between sensors blablabla";
    					Train_pos_event pos = new Train_pos_event(true);
    					pos.msg =msg1;
    					pos.setSlotnumber(slot);
    					pos.setSensors(sensors);
    					rHand.fireMyEvent(pos);
    					return msg1;
    			}
    		default: 
    			int[] dmsg = new int[msg.length];
    			for(int i=0; i<msg.length; i++){
    				dmsg[i] = msg[i];
    			}
    			msg1 = "cannot decrypt this message "+dmsg; 
    			Bad_message_event bad = new Bad_message_event(true);
    			bad.msg = msg1;
    			bad.setMessage(dmsg);
    			return msg1;
    			//break;
    	}
    	//return "nothing";
        
    }
    
    // sends the message to change a switch
    // true for throw, false for close
    // RESTRICTION: connect needs to have been called successfully
    public static boolean changeSwitch(int swnum, boolean state){
    	if(init){
    		try{
    			byte[] msg = makeChangeSwitch(swnum, state);
    			out.write(msg);
    			return true;
    		}catch (java.net.UnknownHostException u) {
                System.out.println("uhe!"); // Display the string.
                System.err.println(u);
                return false;
            }
            catch (java.io.IOException e) {
                System.out.println("ioex!"); // Display the string.
                System.err.println(e);
                return false;
            }
    	}else
    		return false;
    }
    
    // returns an array to be sent for changing a switch
    // true for throw, false for close
    public static byte[] makeChangeSwitch(int swnum, boolean state){
    	byte[] msg = new byte[6];
    	msg[0] = 4;							//msg size
    	msg[1] = 0;							//msg size cont
    	msg[2] = SW_REQ;					//OP code
    	msg[3] = (byte)swnum;				//switchNumber (0-24)
    	if(state)
    		msg[4] = THROW;					//command
    	else
    		msg[4] = CLOSE;					//command
    	msg[5] = checkSum(msg);				//XOR checksum
    	return msg;
    }
    
    // sends a message to get the position of all switches from a controller
    // RESTRICTION: can only be used with a controller
    // RESTRICTION: connect needs to have been called successfully
    public static boolean getSwitches(int swnum, boolean state){
    	if(init){
    		try{
    			byte[] msg = makeGetSwitches();
    			out.write(msg);
    			return true;
    		}catch (java.net.UnknownHostException u) {
                System.out.println("uhe!"); // Display the string.
                System.err.println(u);
                return false;
            }
            catch (java.io.IOException e) {
                System.out.println("ioex!"); // Display the string.
                System.err.println(e);
                return false;
            }
    	}else
    		return false;
    }
    
    // returns an array which requests the position of all switches from a controller
    // RESTRICTION: can only be used with a controller
    public static byte[] makeGetSwitches(){
    	byte[] msg = new byte[4];
    	msg[0] = 2;
    	msg[1] = 0;
    	msg[2] = 0;
    	msg[3] = GETSWITCHSTATE;
    	return msg;
    }
    
    // sends a message to take control of a train
    // RESTRICTION: will not take a train if it is in use by another throttle
    // RESTRICTION: connect needs to have been called successfully
    public static boolean controlTrain(int trainaddress){
    	if(init){
    		try{
    			byte[] msg = makeControlTrain(trainaddress);
    			out.write(msg);
    			return true;
    		}catch (java.net.UnknownHostException u) {
                System.out.println("uhe!"); // Display the string.
                System.err.println(u);
                return false;
            }
            catch (java.io.IOException e) {
                System.out.println("ioex!"); // Display the string.
                System.err.println(e);
                return false;
            }
    	}else
    		return false;
    }
    
    // returns an array which gets control of a train slot
    // RESTRICTION: will not take a train if it is in use by another throttle
    public static byte[] makeControlTrain(int trainaddress){
    	byte[] msg = new byte[6];
    	msg[0] = 4;							//msg size
    	msg[1] = 0;							//msg size cont
    	msg[2] = SLOT_REQ;					//OP code
    	msg[3] = (byte)(trainaddress%128);	//physical train address
    	msg[4] = (byte)(trainaddress/128);
    	msg[4] = checkSum(msg);				//XOR checksum
    	return msg;
    }
    
    // sends a message to take a train from another throttle
    // RESTRICTION: will not take a train if it is not in use by another throttle
    // RESTRICTION: connect needs to have been called successfully
    public static boolean stealTrain(int slot1, int slot2){
    	if(init){
    		try{
    			byte[] msg = makeStealTrain(slot1, slot2);
    			out.write(msg);
    			return true;
    		}catch (java.net.UnknownHostException u) {
                System.out.println("uhe!"); // Display the string.
                System.err.println(u);
                return false;
            }
            catch (java.io.IOException e) {
                System.out.println("ioex!"); // Display the string.
                System.err.println(e);
                return false;
            }
    	}else
    		return false;
    }
    
    // returns an array which takes a train from another throttle for use
    // RESTRICTION: will not take a train if it is not in use by another throttle
    public static byte[] makeStealTrain(int slot1, int slot2){
    	byte[] msg = new byte[6];
    	msg[0] = 4;							//msg size
    	msg[1] = 0;							//msg size cont
    	msg[2] = REG_SLOT;					//OP code
    	msg[3] = (byte)slot1;				//slot of train already in use?
    	msg[4] = (byte)slot2;				//slot of train to be used?
    	msg[4] = checkSum(msg);				//XOR checksum
    	return msg;
    }
    
    // sends a message that sets a slot's speed, max is 127
    // RESTRICTION: connect needs to have been called successfully
    public static boolean setSpeed(int slot, int speed){
    	if(init){
    		try{
    			byte[] msg = makeSetSpeed(slot, speed);
    			out.write(msg);
    			return true;
    		}catch (java.net.UnknownHostException u) {
                System.out.println("uhe!"); // Display the string.
                System.err.println(u);
                return false;
            }
            catch (java.io.IOException e) {
                System.out.println("ioex!"); // Display the string.
                System.err.println(e);
                return false;
            }
    	}else
    		return false;
    }
    
    // returns an array which sets a speed for a certain slot, max is 127
    public static byte[] makeSetSpeed(int slot, int speed){
    	byte[] msg = new byte[6];
    	msg[0] = 4;							//msg size
    	msg[1] = 0;							//msg size cont
    	msg[2] = SET_SPEED;					//OP code
    	msg[3] = (byte)slot;				//slot number
    	msg[4] = (byte)speed;				//speed amount
    	msg[5] = checkSum(msg);				//XOR checksum
    	return msg;
    }

    // sends a message that turns all sounds on or off for a slot
    // RESTRICTION: connect needs to have been called successfully
    public static boolean setMute(int slot, boolean mute){
    	if(init){
    		try{
    			byte[] msg = makeSetMute(slot, mute);
    			out.write(msg);
    			return true;
    		}catch (java.net.UnknownHostException u) {
                System.out.println("uhe!"); // Display the string.
                System.err.println(u);
                return false;
            }
            catch (java.io.IOException e) {
                System.out.println("ioex!"); // Display the string.
                System.err.println(e);
                return false;
            }
    	}else
    		return false;
    }
    
    // returns an array which turns all sound off for a slot
    public static byte[] makeSetMute(int slot, boolean mute){
    	byte[] msg = new byte[6];
    	msg[0] = 4;								//msg size
    	msg[1] = 0;								//msg size cont
    	msg[2] = SET_SOUND;						//OP code
    	msg[3] = (byte)slot;					//slot number
    	if(mute)
    		msg[4] = MUTEON;				//command
    	else
    		msg[4] = 0;
    	msg[5] = checkSum(msg);					//XOR checksum
    	return msg;
    }
    
    // sends a message that changes the direction the train is going, and whether various features are on or off, true for on/false for off
    // Forward is false, Backward is true
    // RESTRICTION: connect needs to have been called successfully
    public static boolean setMisc(int slot, boolean dir, boolean horn, boolean bell, boolean light){
    	if(init){
    		try{
    			byte[] msg = makeSetMisc(slot, dir, horn, bell, light);
    			out.write(msg);
    			return true;
    		}catch (java.net.UnknownHostException u) {
                System.out.println("uhe!"); // Display the string.
                System.err.println(u);
                return false;
            }
            catch (java.io.IOException e) {
                System.out.println("ioex!"); // Display the string.
                System.err.println(e);
                return false;
            }
    	}else
    		return false;
    }
    
    // returns an array which changes the direction the train is going, and whether various features are on or off, true for on/false for off
    // Forward is false, Backward is true
    public static byte[] makeSetMisc(int slot, boolean dir, boolean horn, boolean bell, boolean light){
    	byte[] msg = new byte[6];
    	byte misc = 0;
    	msg[0] = 4;
    	msg[1] = 0;
    	msg[2] = SET_MISC;
    	msg[3] = (byte)slot;
    	if(dir)
    		misc = (byte)(misc | BACKWARD);
    	if(horn)
    		misc = (byte)(misc | HORNON);
    	if(bell)
    		misc = (byte)(misc | BELLON);
    	if(light)
    		misc = (byte)(misc | LIGHTSON);
    	msg[4] = misc;
    	msg[5] = checkSum(msg);	
    	return msg;
    }
    
    ////////////////////////////////////////////////////////////////////// 
    /* Cannot use, would turn off all others beside the one being set
    // turns the light on or off for a slot
    public static byte[] setLight(int slot, boolean light){
    	byte[] msg = new byte[6];
    	msg[0] = 3;								//msg size
    	msg[1] = 0;								//msg size cont
    	msg[2] = SET_MISC;						//OP code
    	msg[3] = (byte)slot;					// slot number
    	if(light)
    		msg[4] = LIGHTSON;					// command
    	else
    		msg[4] = LIGHTSOFF;
    	msg[4] = checkSum(msg);					//XOR checksum
    	return msg;
    }
    
    // sets whether the train is going forward or backward,
    // true for backward, false for forward
    public static byte[] setDirection(int slot, boolean dir){
    	byte[] msg = new byte[6];
    	msg[0] = 3;								//msg size
    	msg[1] = 0;								//msg size cont
    	msg[2] = SET_MISC;						//OP code
    	msg[3] = (byte)slot;					//slot number
    	if(dir)
    		msg[4] = BACKWARD;					//command
    	else
    		msg[4] = FORWARD;
    	msg[4] = checkSum(msg);					//XOR checksum
    	return msg;
    }
    
    // turns the horns on or off
    public static byte[] setHorn(int slot, boolean horn){
    	byte[] msg = new byte[6];
    	msg[0] = 3;								//msg size
    	msg[1] = 0;								//msg size cont
    	msg[2] = SET_MISC;						//OP code
    	msg[3] = (byte)slot;					//slot number
    	if(horn)
    		msg[4] = HORNON;					//command
    	else
    		msg[4] = HORNOFF;
    	msg[4] = checkSum(msg);					//XOR checksum
    	return msg;
    }
    
    // turns the bell on or off
    public static byte[] setBell(int slot, boolean bell){
    	byte[] msg = new byte[6];
    	msg[0] = 3;								//msg size
    	msg[1] = 0;								//msg size cont
    	msg[2] = SET_MISC;						//OP code
    	msg[3] = (byte)slot;					//slot number
    	if(bell)
    		msg[4] = BELLON;	 				//command
    	else
    		msg[4] = BELLOFF;
    	msg[4] = checkSum(msg);					//XOR checksum
    	return msg;
    }
    */
    //////////////////////////////////////////////////////////////////
    	
    // an xor checksum of indexes 2-(end-1) of a message
    public static byte checkSum(byte[] msg){
    	byte chk = 0xFF-256;
    	for(int i=2; i<msg.length-1; i++){
    		chk ^= msg[i];
    	}
    	return chk;
    }

}